import { get, set, unset } from "lodash-es";
import type { FormProps, FormSchemaInner as FormSchema } from "../types/form";
import { isArray, isEmpty, isFunction, isNil, isObject, isString } from "@/utils/is";
import moment from "moment";
import { FormInstance } from "antd";
import { useEffect, useRef } from "react";

interface UseFormValuesContext {
	defaultValueRef: any;
	getSchema: FormSchema[];
	getProps: FormProps;
	formElRef: FormInstance;
}

/**
 * @description deconstruct array-link key. This method will mutate the target.
 */
function tryDeconstructArray(key: string, value: any, target: Recordable) {
	const pattern = /^\[(.+)\]$/;
	if (pattern.test(key)) {
		const match = key.match(pattern);
		if (match && match[1]) {
			const keys = match[1].split(",");
			value = Array.isArray(value) ? value : [value];
			keys.forEach((k, index) => {
				set(target, k.trim(), value[index]);
			});
			return true;
		}
	}
}

/**
 * @description deconstruct object-link key. This method will mutate the target.
 */
function tryDeconstructObject(key: string, value: any, target: Recordable) {
	const pattern = /^\{(.+)\}$/;
	if (pattern.test(key)) {
		const match = key.match(pattern);
		if (match && match[1]) {
			const keys = match[1].split(",");
			value = isObject(value) ? value : {};
			keys.forEach(k => {
				set(target, k.trim(), value[k.trim()]);
			});
			return true;
		}
	}
}

export function useFormValues({ getProps, formElRef }: UseFormValuesContext) {
	// store variable.
	const getPropsRef = useRef<any>(getProps);
	// watch change
	useEffect(() => {
		getPropsRef.current = getProps;
		return () => {
			getPropsRef.current = null;
		};
	}, [getProps]);

	// Processing form values
	function handleFormValues(values: Recordable) {
		if (!isObject(values)) return {};

		const res: Recordable = {};
		for (const item of Object.entries(values)) {
			let [, value] = item;
			const [key] = item;
			if (!key || (isArray(value) && value.length === 0) || isFunction(value)) continue;

			const transformDateFunc = getPropsRef.current.transformDateFunc;
			if (isObject(value)) value = transformDateFunc?.(value);

			if (isArray(value) && value[0]?.format && value[1]?.format) value = value.map(item => transformDateFunc?.(item));

			// Remove spaces
			if (isString(value)) {
				// remove params from URL
				if (value === "") value = value.trim();
			}
			if (!tryDeconstructArray(key, value, res) && !tryDeconstructObject(key, value, res)) {
				// 没有解构成功的，按原样赋值
				set(res, key, value);
			}
		}
		return handleRangeTimeValue(res);
	}

	/**
	 * @description: Processing time interval parameters
	 */
	function handleRangeTimeValue(values: Recordable) {
		const fieldMapToTime = getPropsRef.current.fieldMapToTime;

		if (!fieldMapToTime || !Array.isArray(fieldMapToTime)) return values;

		for (const [field, [startTimeKey, endTimeKey], format = "YYYY-MM-DD"] of fieldMapToTime) {
			if (!field || !startTimeKey || !endTimeKey) continue;

			// If the value to be converted is empty, remove the field
			if (!get(values, field)) {
				unset(values, field);
				continue;
			}

			const [startTime, endTime]: string[] = get(values, field);

			const [startTimeFormat, endTimeFormat] = Array.isArray(format) ? format : [format, format];

			if (!isNil(startTime) && !isEmpty(startTime)) set(values, startTimeKey, formatTime(startTime, startTimeFormat));

			if (!isNil(startTime) && !isEmpty(startTime)) set(values, endTimeKey, formatTime(endTime, endTimeFormat));

			unset(values, field);
		}

		return values;
	}

	function formatTime(time: string, format: string) {
		if (format === "timestamp") return moment(time).unix();
		else if (format === "timestampStartDay") return moment(time).startOf("day").unix();

		return moment(time).format(format);
	}

	function initDefault() {
		if (getPropsRef.current.initialValues) {
			formElRef.setFieldsValue(getPropsRef.current.initialValues);
		}
	}

	return { handleFormValues, initDefault };
}
